unit DebuggerInterface;
{
This unit contains the base class description for the debugger interface.
The other debugger interfaces are inherited from this
}

{$mode delphi}

interface

uses
  Classes, SysUtils{$ifdef windows},windows{$endif}{$ifndef STANDALONEDEBUG},NewKernelHandler, debuggertypedefinitions{$endif}{$ifdef darwin}, macport, macportdefines{$endif};

type
  TDebuggerCapabilities=(dbcHardwareBreakpoint, dbcSoftwareBreakpoint, dbcExceptionBreakpoint, dbcDBVMBreakpoint, dbcBreakOnEntry, dbcCanUseInt1BasedBreakpoints, dbcDealsWithBreakpointsInternally);
  TDebuggerCapabilitiesSet=set of TDebuggerCapabilities;
  TDebuggerInterface=class
  private
    status: string;
    statusmrew: TMultiReadExclusiveWriteSynchronizer;
    function getCurrentDebugggerAttachStatus: string;
    procedure setCurrentDebuggerAttachStatus(newstatus: string);
  protected
    fwatchpointsTriggerAfterExecution: boolean;
    fmaxInstructionBreakpointCount: integer;
    fmaxWatchpointBreakpointCount: integer;
    fmaxSharedBreakpointCount: integer;

    fDebuggerCapabilities: TDebuggerCapabilitiesSet;
    fErrorString: string;
    noBreakList: array of thandle;
  public
    name: string;

    constructor create;
    destructor destroy; override;

    function WaitForDebugEvent(var lpDebugEvent: TDebugEvent; dwMilliseconds: DWORD): BOOL; virtual; abstract;
    function ContinueDebugEvent(dwProcessId: DWORD; dwThreadId: DWORD; dwContinueStatus: DWORD): BOOL; virtual; abstract;
    function SetThreadContext(hThread: THandle; const lpContext: TContext; isFrozenThread: Boolean=false): BOOL; overload; virtual;
    function SetThreadContext(hThread: THandle; lpContext: pointer; isFrozenThread: Boolean=false): BOOL; overload; virtual;
    {$ifndef STANDALONEDEBUG}
    function SetThreadContextArm(hThread: THandle; const lpContext: TARMCONTEXT; isFrozenThread: Boolean=false): BOOL; virtual;
    function SetThreadContextArm64(hThread: THandle; const lpContext: TARM64CONTEXT; isFrozenThread: Boolean=false): BOOL; virtual;
    {$endif}
    function GetThreadContext(hThread: THandle; var lpContext: TContext; isFrozenThread: Boolean=false): BOOL; overload; virtual;
    function GetThreadContext(hThread: THandle; lpContext: pointer; isFrozenThread: Boolean=false): BOOL; overload; virtual;
    {$ifndef STANDALONEDEBUG}
    function GetThreadContextArm(hThread: THandle; var lpContext: TARMCONTEXT; isFrozenThread: Boolean=false): BOOL; virtual;
    function GetThreadContextArm64(hThread: THandle; var lpContext: TARM64CONTEXT; isFrozenThread: Boolean=false): BOOL; virtual;
    {$endif}
    function DebugActiveProcess(dwProcessId: DWORD): BOOL; virtual; abstract;
    function DebugActiveProcessStop(dwProcessID: DWORD): BOOL; virtual;
    function GetLastBranchRecords(lbr: pointer): integer; virtual;
    function isInjectedEvent: boolean; virtual;
    function usesDebugRegisters: boolean; virtual;

    function inNoBreakList(threadid: integer): boolean; virtual;
    procedure AddToNoBreakList(threadid: integer); virtual;
    procedure RemoveFromNoBreakList(threadid: integer); virtual;

    function canUseIPT: boolean; virtual;
    function canReportExactDebugRegisterTrigger: boolean; virtual;
    function needsToAttach: boolean; virtual;
    function controlsTheThreadList: boolean; virtual;

    property DebuggerCapabilities: TDebuggerCapabilitiesSet read fDebuggerCapabilities;
    property errorstring: string read ferrorstring;

    property maxInstructionBreakpointCount: integer read fmaxInstructionBreakpointCount;
    property maxWatchpointBreakpointCount: integer read fmaxWatchpointBreakpointCount;
    property maxSharedBreakpointCount: integer read fmaxSharedBreakpointCount;
    property watchpointsTriggerAfterExecution: boolean read fwatchpointsTriggerAfterExecution;

    property debuggerAttachStatus: string read getCurrentDebugggerAttachStatus write setCurrentDebuggerAttachStatus; //threadsafe getter/setter for a string
end;

const
  DBG_CONTINUE_SINGLESTEP = $00010003; //continueDebugEvent continuestatus only for DBVM BP's (and gdbserver protocol)
  DBG_CONTINUE_SINGLESTEP_UNHANDLED = $00010004;


implementation

function TDebuggerInterface.getCurrentDebugggerAttachStatus: string;
begin
  if statusmrew<>nil then
  begin
    statusmrew.Beginread;
    result:=status;
    statusmrew.Endread;
  end;
end;

procedure TDebuggerInterface.setCurrentDebuggerAttachStatus(newstatus: string);
begin
  if statusmrew<>nil then
  begin
    statusmrew.Beginwrite;
    status:=newstatus;
    statusmrew.Endwrite;
  end;
end;

function TDebuggerInterface.SetThreadContext(hThread: THandle; const lpContext: TContext; isFrozenThread: Boolean=false): BOOL;
begin
  result:=false;
end;

function TDebuggerInterface.SetThreadContext(hThread: THandle; lpContext: pointer; isFrozenThread: Boolean=false): BOOL;
begin
  result:=SetThreadContext(hThread, PContext(lpContext)^, isFrozenThread);
end;

{$ifndef STANDALONEDEBUG}
function TDebuggerInterface.SetThreadContextArm(hThread: THandle; const lpContext: TARMCONTEXT; isFrozenThread: Boolean=false): BOOL;
begin
  result:=false;
end;

function TDebuggerInterface.SetThreadContextArm64(hThread: THandle; const lpContext: TARM64CONTEXT; isFrozenThread: Boolean=false): BOOL;
begin
  result:=false;
end;

function TDebuggerInterface.GetThreadContextArm(hThread: THandle; var lpContext: TARMCONTEXT; isFrozenThread: Boolean=false): BOOL;
begin
  result:=false;
end;

function TDebuggerInterface.GetThreadContextArm64(hThread: THandle; var lpContext: TARM64CONTEXT; isFrozenThread: Boolean=false): BOOL;
begin
  result:=false;
end;
{$endif}

function TDebuggerInterface.GetThreadContext(hThread: THandle; var lpContext: TContext; isFrozenThread: Boolean=false): BOOL;
begin
  result:=false;
end;

function TDebuggerInterface.GetThreadContext(hThread: THandle; lpContext: pointer; isFrozenThread: Boolean=false): BOOL;
begin
  result:=GetThreadContext(hthread, PContext(lpContext)^,isFrozenThread);
end;


function TDebuggerInterface.DebugActiveProcessStop(dwProcessID: DWORD): BOOL;
begin
  //don't complain if not implemented
  result:=true;
end;

function TDebuggerInterface.GetLastBranchRecords(lbr: pointer): integer;
begin
  //if implemented fill in the lbr pointer with the lbr records (array of qwords) and return the count (max 16)
  result:=-1;
end;

function TDebuggerInterface.isInjectedEvent: boolean;
begin
  result:=false;
end;

function TDebuggerInterface.inNoBreakList(threadid: integer): boolean;
var i: integer;
begin
  result:=false;
  for i:=0 to length(nobreaklist)-1 do
    if nobreaklist[i]=threadid then
    begin
      result:=true;
      exit;
    end;
end;

procedure TDebuggerInterface.AddToNoBreakList(threadid: integer);
begin
  if inNoBreaklist(threadid) then exit;

  setlength(nobreaklist, length(nobreaklist)+1);
  nobreaklist[length(nobreaklist)-1]:=threadid;
end;

procedure TDebuggerInterface.RemoveFromNoBreakList(threadid: integer);
var i,j: integer;
begin
  for i:=0 to length(noBreakList)-1 do
  begin
    if nobreaklist[i]=threadid then
    begin
      for j:=i to length(nobreaklist)-2 do
        nobreaklist[j]:=nobreaklist[j+1];

      setlength(nobreaklist, length(nobreaklist)-1);
    end;
  end;
end;

function TDebuggerInterface.canUseIPT: boolean;
begin
  result:=false;
end;

function TDebuggerInterface.canReportExactDebugRegisterTrigger: boolean;
begin
  result:=true;
end;

function TDebuggerInterface.needsToAttach: boolean;
begin
  result:=true;
end;

function TDebuggerInterface.controlsTheThreadList: boolean;
begin
  result:=true;
end;

function TDebuggerInterface.usesDebugRegisters: boolean;
begin
  result:=true;
end;

constructor TDebuggerInterface.create;
begin
  fwatchpointsTriggerAfterExecution:=true;
  fDebuggerCapabilities:=[dbcCanUseInt1BasedBreakpoints]; //all except DBVM (which will remove this flags)

  status:='Before attach';

  statusmrew:=TMultiReadExclusiveWriteSynchronizer.Create;
end;

destructor TDebuggerInterface.destroy;
begin
  freeandnil(statusmrew);
end;

end.

